package MONITORING::SYS::DISKUSAGE;

#################################################################
# $LastChangedDate: 2013-11-25 15:03:06 +0000 (Mon, 25 Nov 2013) $
# $LastChangedRevision: 22 $
# $LastChangedBy: aswod_83@gmx.net $
#################################################################
use strict;
use base qw(moniCA);
use XML::Simple;
use Filesys::DfPortable;
use Data::Dumper;
use Sys::Hostname;
use Params::Validate qw(:all);
use Getopt::Long;

#-------------------------------------------------------------------------------
sub Run {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms: None
#-------------------------------------------------------------------------------
    my $self      = shift;
    my $cnf       = $self->GetCNF();
    my $logger    = $self->GetLogger();
    my $statefile = $self->TmpDir() . '/' . $self->Plug() . '.xml';
    my %newstat;

    #-------------------------------------
    #	validation of default parameters
    #-------------------------------------
    $self->_validCNFdefaults( $cnf->{defaults} );

    #-------------------------------------
    #	validation of identifier parameters
    #-------------------------------------
    $self->_validCNFidentifierAll();

    #-------------------------------------
    #	pass through configured identifier
    #-------------------------------------
    foreach ( keys %{ $self->{IdCfg} } ) {

        $self->{ID} = $_;
        Log::Log4perl::MDC->put( 'identifier', $self->{ID} );
        my %tmpstat;
        my $oldstat = XMLin( $statefile, ForceArray => 1 )->{identifier}->{$_}
          if ( -s $statefile );
        my $failPct = $self->{IdCfg}->{$_}->{'failPct'}
          || $self->{DefCfg}->{'failPct'};
        my $warnPct = $self->{IdCfg}->{$_}->{'warnPct'}
          || $self->{DefCfg}->{'warnPct'};
        my $report = $self->{IdCfg}->{$_}->{'report'}
          || $self->{DefCfg}->{'report'};
        my $trigger = $self->{IdCfg}->{$_}->{'trigger'}
          || $self->{DefCfg}->{'trigger'};

        $self->{identifier}->{$_} = dfportable( "$_", 1024 )
          || $logger->error("$_ not accessible");  # Display output in 1K blocks

        $tmpstat{values} = $self->{identifier}->{$_};

        $self->_validCNFrrd( $cnf->{rrd} ) && $self->_crReport()
          if ( $report =~ /on/i );

        if ( $self->{identifier}->{$_}->{per} > $failPct ) {
            $tmpstat{failCnt} = ( $oldstat->{failCnt} + 1 ) || 1;
            if ( $tmpstat{failCnt} == $trigger ) {
                $logger->error( "tEvent: [ID:$_] [usedPct:"
                      . $self->{identifier}->{$_}->{per}
                      . "%|failPct:"
                      . $failPct
                      . "%]" );
            }
            else {
                $logger->error( "iEvent: [ID:$_] [usedPct:"
                      . $self->{identifier}->{$_}->{per}
                      . "%|failPct:"
                      . $failPct
                      . "%]" );
            }
        }
        else {
            if ( $oldstat->{failCnt} >= $trigger ) {
                $logger->info( "cEvent: [ID:$_] [usedPct:"
                      . $self->{identifier}->{$_}->{per}
                      . "%|failPct:"
                      . $failPct
                      . "%]" );
            }
            $tmpstat{failCnt} = 0;
            if ( $self->{identifier}->{$_}->{per} > $warnPct ) {
                $tmpstat{warnCnt} = ( $oldstat->{warnCnt} + 1 ) || 1;
                if ( $tmpstat{warnCnt} == $trigger ) {
                    $logger->warn( "tEvent: [ID:$_] [usedPct:"
                          . $self->{identifier}->{$_}->{per}
                          . "%|warnPct:"
                          . $warnPct
                          . "%]" );
                }
                else {
                    $logger->warn( "iEvent: [ID:$_] [usedPct:"
                          . $self->{identifier}->{$_}->{per}
                          . "%|warnPct:"
                          . $warnPct
                          . "%]" );
                }
            }
            else {
                if ( $oldstat->{warnCnt} >= $trigger ) {
                    $logger->info( "cEvent: [ID:$_] [usedPct:"
                          . $self->{identifier}->{$_}->{per}
                          . "%|warnPct:"
                          . $warnPct
                          . "%]" );
                }
                $tmpstat{warnCnt} = 0;
                $logger->info( "iEvent: [ID:$_] [usedPct:"
                      . $self->{identifier}->{$_}->{per}
                      . "%]" );
            }
        }
        $newstat{identifier}{$_} = \%tmpstat;
    }
    XMLout( \%newstat, outputfile => $statefile, NoSort => 0, XMLDecl => 1 );
}

#-------------------------------------------------------------------------------
sub _crReport {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------

    my $self   = shift;
    my $logger = $self->GetLogger();

    eval {

        require RRDs;

        ( my $rrdident = $self->{ID} ) =~ s/\W/_/g;

        my $rrdfile =
            $self->{RRDCfg}->{rrdfilepath} . '/'
          . $self->Plug() . '_'
          . $rrdident . '.rrd'
          if ( -d $self->{RRDCfg}->{rrdfilepath} ) || die($!);
        my $rrdimg =
            $self->{RRDCfg}->{rrdimgpath} . '/'
          . $self->Plug() . '_'
          . $rrdident . '.png'
          if ( -d $self->{RRDCfg}->{rrdimgpath} ) || die($!);

        if ( !-f $rrdfile ) {

            my @rrdc;
            push( @rrdc, "$rrdfile" );
            push( @rrdc, "--step=$self->{RRDCfg}->{step}" );
            if ( ref( $self->{RRDCfg}->{DS} ) eq "ARRAY" ) {
                for ( @{ $self->{RRDCfg}->{DS} } ) { push( @rrdc, $_ ); }
            }
            else {
                push( @rrdc, $self->{RRDCfg}->{DS} );
            }
            for ( @{ $self->{RRDCfg}->{RRA} } ) { push( @rrdc, $_ ); }

            RRDs::create(@rrdc);

            $logger->warn( "RRDs::create returns " . RRDs::error() )
              if ( RRDs::error() );
        }

        my @identlist;
        if ( ref( $self->{RRDCfg}->{DS} ) eq "ARRAY" ) {
            for ( @{ $self->{RRDCfg}->{DS} } ) {
                my @tmplist = split( /:/, $_ );
                push( @identlist, $tmplist[1] );
            }
        }
        else {
            my @tmplist = split( /:/, $self->{RRDCfg}->{DS} );
            push( @identlist, $tmplist[1] );
        }
        my $dslist = join( ":", @identlist );

        if ( -f $rrdfile ) {
            my @rrdu;
            my @dsvalue;
            for (@identlist) {
                push( @dsvalue, $self->{identifier}->{ $self->{ID} }->{$_} );
            }
            my $dsvalue = join( ":", @dsvalue );

            push( @rrdu, "$rrdfile" );
            push( @rrdu, "--template=$dslist" );
            push( @rrdu, "N:$dsvalue" );

            RRDs::update(@rrdu);

            $logger->warn( "RRDs::update returns " . RRDs::error() )
              if ( RRDs::error() );

            my @rrdg;
            $rrdfile =~ s/\:\//\\\:\//g if ( $^O eq 'MSWin32' );

            push( @rrdg, "$rrdimg" );
            push( @rrdg, "--title=$self->{RRDCfg}->{'title'}: $self->{ID}" );
            push( @rrdg, "--start=$self->{RRDCfg}->{'start'}" );
            push( @rrdg, "--end=$self->{RRDCfg}->{'end'}" );
            push( @rrdg, "--vertical-label=$self->{RRDCfg}->{'label'}" );
            push( @rrdg, "--imgformat=$self->{RRDCfg}->{'imgformat'}" );
            push( @rrdg, "--watermark=$self->{RRDCfg}->{'watermark'}" );
            push( @rrdg, "--width=$self->{RRDCfg}->{'width'}" );
            push( @rrdg, "--height=$self->{RRDCfg}->{'height'}" );
            push( @rrdg, "--base=$self->{RRDCfg}->{'base'}" );
            push( @rrdg, "--upper-limit=$self->{RRDCfg}->{'upper-limit'}" );
            push( @rrdg, "--lower-limit=$self->{RRDCfg}->{'lower-limit'}" );

            foreach ( keys %{ $self->{RRDCfg}->{DEF} } ) {
                push( @rrdg,
                    "DEF:$_=$rrdfile:$_:$self->{RRDCfg}->{DEF}->{$_}->{CF}" );
                push(
                    @rrdg,
                    "
                           $self->{RRDCfg}->{DEF}->{$_}->{TYPE}:$_#
                           $self->{RRDCfg}->{DEF}->{$_}->{COLOR}:$_ 
                           $self->{RRDCfg}->{DEF}->{$_}->{CF}"
                );
            }

            RRDs::graph(@rrdg);

            $logger->warn( "RRDs::graph returns " . RRDs::error() )
              if ( RRDs::error() );
        }
        1;
    } or $logger->debug( "Report incomplete: " . $@ );

}

#-------------------------------------------------------------------------------
sub _validCNFrrd {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;

    Params::Validate::validation_options( allow_extra => 1 );
    $self->{RRDCfg} = validate(
        @_,
        {
            'rrdfilepath' => {
                type     => SCALAR,
                default  => $self->VarDir(),
                optional => 1,
                regex    => qr/^.+$/
            },
            'rrdimgpath' => {
                type     => SCALAR,
                default  => $self->VarDir() . "/images",
                optional => 1,
                regex    => qr/^.+$/
            },
            'title' => { type => SCALAR },
            'label' => { type => SCALAR },
            'width' =>
              { type => SCALAR, default => "400", regex => qr/\d\d\d/ },
            'height' =>
              { type => SCALAR, default => "100", regex => qr/\d\d\d/ },
            'start'       => { type => SCALAR, default => "end-24hours" },
            'end'         => { type => SCALAR, default => "now" },
            'base'        => { type => SCALAR, default => 1000 },
            'upper-limit' => { type => SCALAR, default => 100 },
            'lower-limit' => { type => SCALAR, default => 0 },
            'imgformat'   => { type => SCALAR, regex   => qr/[PNG||GIF||PDF]/ },
            'watermark' =>
              { type => SCALAR, default => "powered by Andre Schmitt" },
            'DS'  => { type => ARRAYREF | SCALAR },
            'RRA' => { type => ARRAYREF | SCALAR }
        }
    );
    return $self;

}

#-------------------------------------------------------------------------------
sub _validCNFdefaults {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;

    Params::Validate::validation_options( allow_extra => 0 );
    $self->{DefCfg} = validate(
        @_,
        {
            'warnPct' => { type => SCALAR, regex => qr/^\d+$/i },
            'failPct' => { type => SCALAR, regex => qr/^\d+$/i },
            'report'  => { type => SCALAR, regex => qr/^(on||off)$/i },
            'trigger' => { type => SCALAR, regex => qr/^.+$/ }
        }
    );
    return $self;
}

#-------------------------------------------------------------------------------
sub _validCNFidentifier {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;
    my $cnf  = $self->GetCNF();

    Params::Validate::validation_options( allow_extra => 0 );
    $self->{IdCfg}->{ $self->{ID} } = validate(
        @_,
        {
            'warnPct' => { type => SCALAR, regex => qr/^\d+$/i, optional => 1 },
            'failPct' => { type => SCALAR, regex => qr/^\d+$/i, optional => 1 },
            'report' =>
              { type => SCALAR, regex => qr/^(on||off)$/i, optional => 1 },
            'trigger' => { type => SCALAR, regex => qr/^.+$/, optional => 1 }
        }
    );
    return $self;
}

#-------------------------------------------------------------------------------
sub _validCNFidentifierAll {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;
    my $cnf  = $self->GetCNF();

    foreach ( keys %{ $cnf->{identifier} } ) {
        $self->{ID} = $_;
        $self->_validCNFidentifier( $cnf->{identifier}->{$_} );
    }
}
1;
__END__
 
=pod
 
=head1 NAME
 
DISKUSAGE.pm - monitors maximum trashold of diskusage
 
=head1 SYNOPSIS

=head1 OPTIONS

=head1 DESCRIPTION

=head1 PREREQUISITES
 
L<Log::Log4perl>,L<XML::Simple>,L<Filesys::DfPortable>

=head1 CONFIGURATION SAMPLE
 
 Modul			MONITORING::SYS::DISKUSAGE
 Logger			moniCA
 LogLevel		DEBUG
 Subject		moniCA Diskusage / ${APPL_NAME} / ${APPL_LAYER} / ${HOSTNAME}
 Description	This moniCA plug checks ...
 
 #DOC: 
 #DOC: 
 #DOC: 

 <defaults>
         report  = on
         trigger = 2
         failPct = 10
         warnPct = 5
 </defaults>

 #DOC: 
 #DOC: 
 #DOC:	

 <identifier "C://">
         failPct 90
         warnPct 80
 </identifier>
 
 #DOC: 
 #DOC: 
 #DOC: 
 
 <rrd>
         # rrdimgpath  =
         # rrdfilepath =
         step         = 300
         DS           = DS:per:GAUGE:600:0:100
         DS           = DS:bavail:GAUGE:600:0:1000000000
         DS           = DS:bused:GAUGE:600:0:1000000000
         RRA          = RRA:AVERAGE:0.5:1:800
         RRA          = RRA:AVERAGE:0.5:6:800
         RRA          = RRA:AVERAGE:0.5:24:800
         RRA          = RRA:AVERAGE:0.5:288:800
         title        = Filesystem
         label        = Diskusage in % 
         width        = 400
         height       = 100
         start        = end-24hours
         end          = now
       	 base         = 1000
		 upper-limit  = 100 
		 lower-limit  = 0 
         watermark    = powered by Andre Schmitt
         imgformat    = PNG
         <DEF "bused">
                 CF    = AVERAGE
                 TYPE  = LINE2
                 COLOR = FFC0CB
         </DEF>
         <DEF "bavail">
                 CF    = AVERAGE
                 TYPE  = LINE2
                 COLOR = FFC0CB
         </DEF>
 </rrd> 

=cut
